﻿using System;
using System.Collections.Generic;

namespace TLang.Ast
{
    using Values;

    public class SubscriptNode : Node
    {
        private readonly Node _node;
        private readonly Node _keyOrIndex;


        public SubscriptNode(Node node, Node index, String file, int start, int end, int line, int col) : base(file, start, end, line, col)
        {
            this._node = node;
            this._keyOrIndex = index;
        }

        public override Value Interp(Scope s)
        {
            Value value = _node.Interp(s);

            if (value is VectorValue vector)
            {
                Value keyOrIndex = _keyOrIndex.Interp(s);
                if (!(keyOrIndex is IntValue index))
                {
                    throw Util.GeneralError(_node, "subscript " + _keyOrIndex + " is not an integer: " + keyOrIndex);
                }

                int i = index.Value;

                if (i >= 0 && i < vector.Count)
                {
                    return vector[i];
                }
                else
                {
                    throw Util.GeneralError(this, "subscript out of bound: " + i + " v.s. [0, " + (vector.Count - 1) + "]");
                }
            }
            else if (value is RecordType record)
            {
                Scope recordScope = record.Properties;
                if (_keyOrIndex is NameNode nameNode)
                {
                    if (recordScope.ContainsKeyLocal(nameNode.Id))
                    {
                        return recordScope.Lookup(nameNode.Id);
                    }
                }
                Value keyOrIndex = _keyOrIndex.Interp(s);
                if (!(keyOrIndex is StringValue key))
                {
                    throw Util.GeneralError(_node, "subscript " + _keyOrIndex + " is not an string: " + keyOrIndex);
                }
                if (!recordScope.ContainsKeyLocal(key.Value))
                {
                    throw Util.GeneralError(this, "subscript key not found: " + key);
                }
                return recordScope.Lookup(key.Value);
            }
            else
            {
                throw Util.GeneralError(_node, "subscripting must be vector or record: " + value);
            }
        }

        public void Set(Value v, Scope s)
        {
            Value value = _node.Interp(s);

            if (value is VectorValue vector)
            {
                Value keyOrIndex = _keyOrIndex.Interp(s);
                if (!(keyOrIndex is IntValue))
                {
                    throw Util.GeneralError(_node, "subscript " + _keyOrIndex + " is not an integer: " + keyOrIndex);
                }

                int i = ((IntValue)keyOrIndex).Value;

                if (i >= 0 && i < vector.Count)
                {
                    vector[i] = v;
                }
                else
                {
                    throw Util.GeneralError(this, "subscript out of bound: " + i + " v.s. [0, " + (vector.Count - 1) + "]");
                }
            }
            else if (value is RecordType record)
            {
                Scope recordScope = record.Properties;
                if (_keyOrIndex is NameNode nameNode)
                {
                    if (recordScope.ContainsKeyLocal(nameNode.Id))
                    {
                        recordScope.PutValue(nameNode.Id, v);
                        return;
                    }
                }
                Value keyOrIndex = _keyOrIndex.Interp(s);
                if (!(keyOrIndex is StringValue key))
                {
                    throw Util.GeneralError(_node, "subscript " + _keyOrIndex + " is not an string: " + keyOrIndex);
                }
                recordScope.PutValue(key.Value, v);
            }
            else
            {
                throw Util.GeneralError(_node, "subscripting must be vector or record: " + value);
            }
        }


        public override String ToString()
        {
            return _node.ToString() + Constants.DotChar + _keyOrIndex.ToString();
        }
    }
}
